package com.onlyxiahui.aware.multiple.core.lock.impl;

import java.util.Arrays;
import java.util.Collections;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import com.onlyxiahui.aware.multiple.core.lock.DistributeLock;

/**
 * <br>
 * Date 2020-12-21 15:49:01<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */
public class DistributeLockRedis implements DistributeLock {
	/**
	 * 无限重试
	 */
	public static final int UN_LIMIT_RETRY = -1;
	/**
	 * 操作成功标识
	 */
	private static final Long OP_SUCCESS = 1L;
	/**
	 * 默认过期时间 单位：毫秒
	 */
	public static final int DEFAULT_EXPIRE_TIME = 10 * 1000;
	/**
	 * 默认加锁重试时间 单位：毫秒
	 */
	public static final int DEFAULT_RETRY_TIME = 300;
	/**
	 * 默认的加锁重试次数
	 */
	public static final int DEFAULT_RETRY_COUNT = 5;
	/**
	 * lua 脚本 加锁
	 */
	public static final String LUA_SCRIPT_LOCK = "if redis.call('set',KEYS[1],ARGV[1],'EX',ARGV[2],'NX') then return 1 else return 0 end";
	/**
	 * lua 脚本 解锁
	 */
	public static final String LUA_SCRIPT_UNLOCK = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

	@Autowired
	private RedisTemplate<String, String> redisTemplate;

	@Override
	public boolean lock(String lockKey) {
		return lock(lockKey, DEFAULT_EXPIRE_TIME);
	}

	@Override
	public boolean lock(String lockKey, int expireMillisecond) {
		return lock(lockKey, lockKey, expireMillisecond);
	}

	@Override
	public boolean lock(String lockKey, String requestKey) {
		return lock(lockKey, requestKey, DEFAULT_EXPIRE_TIME);
	}

	@Override
	public boolean lock(String lockKey, String requestKey, int expireMillisecond) {

		DefaultRedisScript<Long> redisLuaScript = new DefaultRedisScript<>(LUA_SCRIPT_LOCK, Long.class);
		// List<String> keyList = Arrays.asList(lockKey);
		Object execute = redisTemplate.execute(redisLuaScript, Arrays.asList(lockKey), requestKey, String.valueOf(expireMillisecond));
		// execute = redisTemplate.execute(luaScript.getRequireLockScript(), keyList,
		// requestKey, expireMillisecond + "");

		if (OP_SUCCESS.equals(execute)) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public boolean lockAndRetry(String lockKey) {
		return lockAndRetry(lockKey, DEFAULT_EXPIRE_TIME);
	}

	@Override
	public boolean lockAndRetry(String lockKey, String requestKey) {
		return lockAndRetry(lockKey, requestKey, DEFAULT_EXPIRE_TIME);
	}

	@Override
	public boolean lockAndRetry(String lockKey, int expireMillisecond) {
		return lockAndRetry(lockKey, expireMillisecond, DEFAULT_RETRY_COUNT);
	}

	@Override
	public boolean lockAndRetry(String lockKey, int expireMillisecond, int retryCount) {
		return lockAndRetry(lockKey, lockKey, expireMillisecond, retryCount);
	}

	@Override
	public boolean lockAndRetry(String lockKey, String requestKey, int expireMillisecond) {
		return lockAndRetry(lockKey, requestKey, expireMillisecond, DEFAULT_RETRY_COUNT);
	}

	@Override
	public boolean lockAndRetry(String lockKey, String requestKey, int expireMillisecond, int retryCount) {
		return lockAndRetry(lockKey, requestKey, expireMillisecond, retryCount, DEFAULT_RETRY_TIME);
	}

	@Override
	public boolean lockAndRetry(String lockKey, String requestKey, int expireMillisecond, int retryCount, int retryIntervalMillisecond) {
		if (retryCount <= 0) {
			// retryCount小于等于0 无限循环，一直尝试加锁
			while (true) {
				boolean result = lock(lockKey, requestKey, expireMillisecond);
				if (result) {
					return result;
				}
				try {
					// 休眠一会
					Thread.sleep(retryIntervalMillisecond);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		} else {
			// retryCount大于0 尝试指定次数后，退出
			for (int i = 0; i < retryCount; i++) {
				boolean result = lock(lockKey, requestKey, expireMillisecond);
				if (result) {
					return result;
				}
				try {
					// 休眠一会
					Thread.sleep(retryIntervalMillisecond);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			return false;
		}
	}

	@Override
	public boolean unLock(String lockKey, String requestKey) {

		// List<String> keyList = Arrays.asList(lockKey);
		// Object execute = redisTemplate.execute(luaScript.getReleaseLockScript(),
		// keyList, requestKey);
		DefaultRedisScript<Long> redisLuaScript = new DefaultRedisScript<>(LUA_SCRIPT_UNLOCK, Long.class);
		Object execute = redisTemplate.execute(redisLuaScript, Collections.singletonList(lockKey), requestKey);
		// 释放锁成功
		return OP_SUCCESS.equals(execute);
	}

	@Override
	public boolean unLock(String lockKey) {
		return unLock(lockKey, lockKey);
	}
}
